home *** CD-ROM | disk | FTP | other *** search
/ Almathera Ten Pack 3: CDPD 3 / Almathera Ten on Ten - Disc 3: CDPD3.iso / fish / 001-100 / 001-025 / 022 / lemacs / display.c < prev    next >
C/C++ Source or Header  |  1995-03-17  |  31KB  |  1,294 lines

  1. /*
  2.  * The functions in this file handle redisplay. There are two halves, the
  3.  * ones that update the virtual display screen, and the ones that make the
  4.  * physical display screen the same as the virtual display screen. These
  5.  * functions use hints that are left in the windows by the commands.
  6.  *
  7.  */
  8.  
  9. #include        <stdio.h>
  10. #include    "estruct.h"
  11. #include        "edef.h"
  12.  
  13. #define WFDEBUG 0                       /* Window flag debug. */
  14.  
  15. typedef struct  VIDEO {
  16.         short   v_flag;                 /* Flags */
  17.         char    v_text[1];              /* Screen data. */
  18. }       VIDEO;
  19.  
  20. #define VFCHG   0x0001                  /* Changed flag            */
  21. #define    VFEXT    0x0002            /* extended (beyond column 80)    */
  22. #define    VFREV    0x0004            /* reverse video status        */
  23. #define    VFREQ    0x0008            /* reverse video request    */
  24.  
  25. int     vtrow   = 0;                    /* Row location of SW cursor */
  26. int     vtcol   = 0;                    /* Column location of SW cursor */
  27. int     ttrow   = HUGE;                 /* Row location of HW cursor */
  28. int     ttcol   = HUGE;                 /* Column location of HW cursor */
  29. int    lbound    = 0;            /* leftmost column of current line
  30.                        being displayed */
  31.  
  32. VIDEO   **vscreen;                      /* Virtual screen. */
  33. VIDEO   **pscreen;                      /* Physical screen. */
  34.  
  35. /*
  36.  * Initialize the data structures used by the display code. The edge vectors
  37.  * used to access the screens are set up. The operating system's terminal I/O
  38.  * channel is set up. All the other things get initialized at compile time.
  39.  * The original window has "WFCHG" set, so that it will get completely
  40.  * redrawn on the first call to "update".
  41.  */
  42. vtinit()
  43. {
  44.     register int i;
  45.     register VIDEO *vp;
  46.     char *malloc();
  47.  
  48.     (*term.t_open)();
  49.     (*term.t_rev)(FALSE);
  50.     vscreen = (VIDEO **) malloc(term.t_nrow*sizeof(VIDEO *));
  51.  
  52.     if (vscreen == NULL)
  53.         exit(1);
  54.  
  55.     pscreen = (VIDEO **) malloc(term.t_nrow*sizeof(VIDEO *));
  56.  
  57.     if (pscreen == NULL)
  58.         exit(1);
  59.  
  60.     for (i = 0; i < term.t_nrow; ++i)
  61.         {
  62.         vp = (VIDEO *) malloc(sizeof(VIDEO)+term.t_ncol);
  63.  
  64.         if (vp == NULL)
  65.             exit(1);
  66.  
  67.     vp->v_flag = 0;
  68.         vscreen[i] = vp;
  69.         vp = (VIDEO *) malloc(sizeof(VIDEO)+term.t_ncol);
  70.  
  71.         if (vp == NULL)
  72.             exit(1);
  73.  
  74.     vp->v_flag = 0;
  75.         pscreen[i] = vp;
  76.         }
  77. }
  78.  
  79. /*
  80.  * Clean up the virtual terminal system, in anticipation for a return to the
  81.  * operating system. Move down to the last line and clear it out (the next
  82.  * system prompt will be written in the line). Shut down the channel to the
  83.  * terminal.
  84.  */
  85. vttidy()
  86. {
  87.     mlerase();
  88.     movecursor(term.t_nrow, 0);
  89.     (*term.t_close)();
  90. }
  91.  
  92. /*
  93.  * Set the virtual cursor to the specified row and column on the virtual
  94.  * screen. There is no checking for nonsense values; this might be a good
  95.  * idea during the early stages.
  96.  */
  97. vtmove(row, col)
  98. {
  99.     vtrow = row;
  100.     vtcol = col;
  101. }
  102.  
  103. /*
  104.  * Write a character to the virtual screen. The virtual row and column are
  105.  * updated. If the line is too long put a "$" in the last column. This routine
  106.  * only puts printing characters into the virtual terminal buffers. Only
  107.  * column overflow is checked.
  108.  */
  109. vtputc(c)
  110.     int c;
  111. {
  112.     register VIDEO      *vp;
  113.  
  114.     vp = vscreen[vtrow];
  115.  
  116.     if (vtcol >= term.t_ncol) {
  117.         vtcol = (vtcol + 0x07) & ~0x07;
  118.         vp->v_text[term.t_ncol - 1] = '$';
  119.     } else if (c == '\t')
  120.         {
  121.         do
  122.             {
  123.             vtputc(' ');
  124.             }
  125.         while ((vtcol&0x07) != 0);
  126.         }
  127.     else if (c < 0x20 || c == 0x7F)
  128.         {
  129.         vtputc('^');
  130.         vtputc(c ^ 0x40);
  131.         }
  132.     else
  133.     vp->v_text[vtcol++] = c;
  134. }
  135.  
  136. /*    put a character to the virtual screen in an extended line. If we are
  137.     not yet on left edge, don't print it yet. check for overflow on
  138.     the right margin                        */
  139.  
  140. vtpute(c)
  141.  
  142. int c;
  143.  
  144. {
  145.     register VIDEO      *vp;
  146.  
  147.     vp = vscreen[vtrow];
  148.  
  149.     if (vtcol >= term.t_ncol) {
  150.         vtcol = (vtcol + 0x07) & ~0x07;
  151.         vp->v_text[term.t_ncol - 1] = '$';
  152.     } else if (c == '\t')
  153.         {
  154.         do
  155.             {
  156.             vtpute(' ');
  157.             }
  158.         while (((vtcol + lbound)&0x07) != 0);
  159.         }
  160.     else if (c < 0x20 || c == 0x7F)
  161.         {
  162.         vtpute('^');
  163.         vtpute(c ^ 0x40);
  164.         }
  165.     else {
  166.     if (vtcol >= 0)
  167.         vp->v_text[vtcol] = c;
  168.     ++vtcol;
  169.     }
  170. }
  171.  
  172. /*
  173.  * Erase from the end of the software cursor to the end of the line on which
  174.  * the software cursor is located.
  175.  */
  176. vteeol()
  177. {
  178.     register VIDEO      *vp;
  179.  
  180.     vp = vscreen[vtrow];
  181.     while (vtcol < term.t_ncol)
  182.         vp->v_text[vtcol++] = ' ';
  183. }
  184.  
  185. /*
  186.  * Make sure that the display is right. This is a three part process. First,
  187.  * scan through all of the windows looking for dirty ones. Check the framing,
  188.  * and refresh the screen. Second, make sure that "currow" and "curcol" are
  189.  * correct for the current window. Third, make the virtual and physical
  190.  * screens the same.
  191.  */
  192. update()
  193. {
  194.     register LINE *lp;
  195.     register WINDOW *wp;
  196.     register VIDEO *vp1;
  197.     register VIDEO *vp2;
  198.     register int i;
  199.     register int j;
  200.     register int c;
  201.  
  202. #if    TYPEAH
  203.     if (typahead())
  204.     return(TRUE);
  205. #endif
  206.  
  207.     /* update the reverse video flags for any mode lines out there */
  208.     for (i = 0; i < term.t_nrow; ++i)
  209.         vscreen[i]->v_flag &= ~VFREQ;
  210.  
  211. #if    REVSTA
  212.     wp = wheadp;
  213.     while (wp != NULL) {
  214.         vscreen[wp->w_toprow+wp->w_ntrows]->v_flag |= VFREQ;
  215.         wp = wp->w_wndp;
  216.     }
  217. #endif
  218.  
  219.     wp = wheadp;
  220.  
  221.     while (wp != NULL)
  222.         {
  223.         /* Look at any window with update flags set on. */
  224.  
  225.         if (wp->w_flag != 0)
  226.             {
  227.             /* If not force reframe, check the framing. */
  228.  
  229.             if ((wp->w_flag & WFFORCE) == 0)
  230.                 {
  231.                 lp = wp->w_linep;
  232.  
  233.                 for (i = 0; i < wp->w_ntrows; ++i)
  234.                     {
  235.                     if (lp == wp->w_dotp)
  236.                         goto out;
  237.  
  238.                     if (lp == wp->w_bufp->b_linep)
  239.                         break;
  240.  
  241.                     lp = lforw(lp);
  242.                     }
  243.                 }
  244.  
  245.             /* Not acceptable, better compute a new value for the line at the
  246.              * top of the window. Then set the "WFHARD" flag to force full
  247.              * redraw.
  248.              */
  249.             i = wp->w_force;
  250.  
  251.             if (i > 0)
  252.                 {
  253.                 --i;
  254.  
  255.                 if (i >= wp->w_ntrows)
  256.                   i = wp->w_ntrows-1;
  257.                 }
  258.             else if (i < 0)
  259.                 {
  260.                 i += wp->w_ntrows;
  261.  
  262.                 if (i < 0)
  263.                     i = 0;
  264.                 }
  265.             else
  266.                 i = wp->w_ntrows/2;
  267.  
  268.             lp = wp->w_dotp;
  269.  
  270.             while (i != 0 && lback(lp) != wp->w_bufp->b_linep)
  271.                 {
  272.                 --i;
  273.                 lp = lback(lp);
  274.                 }
  275.  
  276.             wp->w_linep = lp;
  277.             wp->w_flag |= WFHARD;       /* Force full. */
  278.  
  279. out:
  280.             /* Try to use reduced update. Mode line update has its own special
  281.              * flag. The fast update is used if the only thing to do is within
  282.              * the line editing.
  283.              */
  284.             lp = wp->w_linep;
  285.             i = wp->w_toprow;
  286.  
  287.             if ((wp->w_flag & ~WFMODE) == WFEDIT)
  288.                 {
  289.                 while (lp != wp->w_dotp)
  290.                     {
  291.                     ++i;
  292.                     lp = lforw(lp);
  293.                     }
  294.  
  295.                 vscreen[i]->v_flag |= VFCHG;
  296.                 vtmove(i, 0);
  297.  
  298.                 for (j = 0; j < llength(lp); ++j)
  299.                     vtputc(lgetc(lp, j));
  300.  
  301.                 vteeol();
  302.                 }
  303.              else if ((wp->w_flag & (WFEDIT | WFHARD)) != 0)
  304.                 {
  305.                 while (i < wp->w_toprow+wp->w_ntrows)
  306.                     {
  307.                     vscreen[i]->v_flag |= VFCHG;
  308.                     vtmove(i, 0);
  309.  
  310.             /* if line has been changed */
  311.                     if (lp != wp->w_bufp->b_linep)
  312.                         {
  313.                         for (j = 0; j < llength(lp); ++j)
  314.                             vtputc(lgetc(lp, j));
  315.  
  316.                         lp = lforw(lp);
  317.                         }
  318.  
  319.                     vteeol();
  320.                     ++i;
  321.                     }
  322.                 }
  323. #if ~WFDEBUG
  324.             if ((wp->w_flag&WFMODE) != 0)
  325.                 modeline(wp);
  326.  
  327.             wp->w_flag  = 0;
  328.             wp->w_force = 0;
  329. #endif
  330.             }
  331. #if WFDEBUG
  332.         modeline(wp);
  333.         wp->w_flag =  0;
  334.         wp->w_force = 0;
  335. #endif
  336.  
  337.     /* and onward to the next window */
  338.         wp = wp->w_wndp;
  339.         }
  340.  
  341.     /* Always recompute the row and column number of the hardware cursor. This
  342.      * is the only update for simple moves.
  343.      */
  344.     lp = curwp->w_linep;
  345.     currow = curwp->w_toprow;
  346.  
  347.     while (lp != curwp->w_dotp)
  348.         {
  349.         ++currow;
  350.         lp = lforw(lp);
  351.         }
  352.  
  353.     curcol = 0;
  354.     i = 0;
  355.  
  356.     while (i < curwp->w_doto)
  357.         {
  358.         c = lgetc(lp, i++);
  359.  
  360.         if (c == '\t')
  361.             curcol |= 0x07;
  362.         else if (c < 0x20 || c == 0x7F)
  363.             ++curcol;
  364.  
  365.         ++curcol;
  366.         }
  367.  
  368.     if (curcol >= term.t_ncol - 1) {          /* extended line. */
  369.     /* flag we are extended and changed */
  370.     vscreen[currow]->v_flag |= VFEXT | VFCHG;
  371.     updext();                /* and output extended line */
  372.     } else
  373.     lbound = 0;                /* not extended line */
  374.  
  375. /* make sure no lines need to be de-extended because the cursor is
  376.    no longer on them */
  377.  
  378.     wp = wheadp;
  379.  
  380.     while (wp != NULL) {
  381.     lp = wp->w_linep;
  382.     i = wp->w_toprow;
  383.  
  384.     while (i < wp->w_toprow + wp->w_ntrows) {
  385.         if (vscreen[i]->v_flag & VFEXT) {
  386.             /* always flag extended lines as changed */
  387.             vscreen[i]->v_flag |= VFCHG;
  388.             if ((wp != curwp) || (lp != wp->w_dotp) ||
  389.                (curcol < term.t_ncol - 1)) {
  390.                 vtmove(i, 0);
  391.                             for (j = 0; j < llength(lp); ++j)
  392.                                 vtputc(lgetc(lp, j));
  393.                 vteeol();
  394.  
  395.                 /* this line no longer is extended */
  396.                 vscreen[i]->v_flag &= ~VFEXT;
  397.             }
  398.         }
  399.         lp = lforw(lp);
  400.         ++i;
  401.     }
  402.     /* and onward to the next window */
  403.         wp = wp->w_wndp;
  404.     }
  405.  
  406.     /* Special hacking if the screen is garbage. Clear the hardware screen,
  407.      * and update your copy to agree with it. Set all the virtual screen
  408.      * change bits, to force a full update.
  409.      */
  410.     if (sgarbf != FALSE)
  411.         {
  412.         for (i = 0; i < term.t_nrow; ++i)
  413.             {
  414.             vscreen[i]->v_flag |= VFCHG;
  415.             vp1 = pscreen[i];
  416.             for (j = 0; j < term.t_ncol; ++j)
  417.                 vp1->v_text[j] = ' ';
  418.             }
  419.  
  420.         movecursor(0, 0);               /* Erase the screen. */
  421.         (*term.t_eeop)();
  422.         sgarbf = FALSE;                 /* Erase-page clears */
  423.         mpresf = FALSE;                 /* the message area. */
  424.         }
  425.  
  426.     /* Make sure that the physical and virtual displays agree. Unlike before,
  427.      * the "updateline" code is only called with a line that has been updated
  428.      * for sure.
  429.      */
  430.     for (i = 0; i < term.t_nrow; ++i)
  431.         {
  432.         vp1 = vscreen[i];
  433.  
  434.     /* for each line that needs to be updated, or that needs its
  435.        reverse video status changed, call the line updater    */
  436.     j = vp1->v_flag;
  437.         if (((j & VFCHG) != 0) || (((j & VFREV) == 0) != ((j & VFREQ) == 0)))
  438.             {
  439. #if    TYPEAH
  440.         if (typahead())
  441.         return(TRUE);
  442. #endif
  443.             vp2 = pscreen[i];
  444.             updateline(i, &vp1->v_text[0], &vp2->v_text[0], &vp1->v_flag);
  445.             }
  446.         }
  447.  
  448.     /* Finally, update the hardware cursor and flush out buffers. */
  449.  
  450.     movecursor(currow, curcol - lbound);
  451.     (*term.t_flush)();
  452. }
  453.  
  454. /*    updext: update the extended line which the cursor is currently
  455.         on at a column greater than the terminal width. The line
  456.         will be scrolled right or left to let the user see where
  457.         the cursor is
  458.                                 */
  459.  
  460. updext()
  461.  
  462. {
  463.     register int rcursor;    /* real cursor location */
  464.     register LINE *lp;    /* pointer to current line */
  465.     register int j;        /* index into line */
  466.  
  467.     /* calculate what column the real cursor will end up in */
  468.     rcursor = ((curcol - term.t_ncol) % term.t_scrsiz) + term.t_margin;
  469.     lbound = curcol - rcursor + 1;
  470.  
  471.     /* scan through the line outputing characters to the virtual screen */
  472.     /* once we reach the left edge                    */
  473.     vtmove(currow, -lbound);    /* start scanning offscreen */
  474.     lp = curwp->w_dotp;        /* line to output */
  475.     for (j=0; j<llength(lp); ++j)    /* until the end-of-line */
  476.         vtpute(lgetc(lp, j));
  477.  
  478.     /* truncate the virtual line */
  479.     vteeol();
  480.  
  481.     /* and put a '$' in column 1 */
  482.     vscreen[currow]->v_text[0] = '$';
  483. }
  484.  
  485. /*
  486.  * Update a single line. This does not know how to use insert or delete
  487.  * character sequences; we are using VT52 functionality. Update the physical
  488.  * row and column variables. It does try an exploit erase to end of line. The
  489.  * RAINBOW version of this routine uses fast video.
  490.  */
  491. updateline(row, vline, pline, flags)
  492.     char vline[];    /* what we want it to end up as */
  493.     char pline[];    /* what it looks like now       */
  494.     short *flags;    /* and how we want it that way  */
  495. {
  496. #if RAINBOW
  497.     register char *cp1;
  498.     register char *cp2;
  499.     register int nch;
  500.  
  501.     /* since we don't know how to make the rainbow do this, turn it off */
  502.     flags &= (~VFREV & ~VFREQ);
  503.  
  504.     cp1 = &vline[0];                    /* Use fast video. */
  505.     cp2 = &pline[0];
  506.     putline(row+1, 1, cp1);
  507.     nch = term.t_ncol;
  508.  
  509.     do
  510.         {
  511.         *cp2 = *cp1;
  512.         ++cp2;
  513.         ++cp1;
  514.         }
  515.     while (--nch);
  516.     *flags &= ~VFCHG;
  517. #else
  518.     register char *cp1;
  519.     register char *cp2;
  520.     register char *cp3;
  521.     register char *cp4;
  522.     register char *cp5;
  523.     register int nbflag;    /* non-blanks to the right flag? */
  524.     int rev;        /* reverse video flag */
  525.     int req;        /* reverse video request flag */
  526.  
  527.  
  528.     /* set up pointers to virtual and physical lines */
  529.     cp1 = &vline[0];
  530.     cp2 = &pline[0];
  531.  
  532. #if    REVSTA
  533.     /* if we need to change the reverse video status of the
  534.        current line, we need to re-write the entire line     */
  535.     rev = *flags & VFREV;
  536.     req = *flags & VFREQ;
  537.     if (rev != req) {
  538.         movecursor(row, 0);    /* Go to start of line. */
  539.         (*term.t_rev)(req != FALSE);    /* set rev video if needed */
  540.  
  541.         /* scan through the line and dump it to the screen and
  542.            the virtual screen array                */
  543.         cp3 = &vline[term.t_ncol];
  544.         while (cp1 < cp3) {
  545.             (*term.t_putchar)(*cp1);
  546.             ++ttcol;
  547.             *cp2++ = *cp1++;
  548.         }
  549.         (*term.t_rev)(FALSE);        /* turn rev video off */
  550.  
  551.         /* update the needed flags */
  552.         *flags &= ~VFCHG;
  553.         if (req)
  554.             *flags |= VFREV;
  555.         else
  556.             *flags &= ~VFREV;
  557.         return(TRUE);
  558.     }
  559. #endif
  560.  
  561.     /* advance past any common chars at the left */
  562.     while (cp1 != &vline[term.t_ncol] && cp1[0] == cp2[0]) {
  563.         ++cp1;
  564.         ++cp2;
  565.     }
  566.  
  567. /* This can still happen, even though we only call this routine on changed
  568.  * lines. A hard update is always done when a line splits, a massive
  569.  * change is done, or a buffer is displayed twice. This optimizes out most
  570.  * of the excess updating. A lot of computes are used, but these tend to
  571.  * be hard operations that do a lot of update, so I don't really care.
  572.  */
  573.     /* if both lines are the same, no update needs to be done */
  574.     if (cp1 == &vline[term.t_ncol])
  575.         return(TRUE);
  576.  
  577.     /* find out if there is a match on the right */
  578.     nbflag = FALSE;
  579.     cp3 = &vline[term.t_ncol];
  580.     cp4 = &pline[term.t_ncol];
  581.  
  582.     while (cp3[-1] == cp4[-1]) {
  583.         --cp3;
  584.         --cp4;
  585.         if (cp3[0] != ' ')        /* Note if any nonblank */
  586.             nbflag = TRUE;        /* in right match. */
  587.     }
  588.  
  589.     cp5 = cp3;
  590.  
  591.     if (nbflag == FALSE && eolexist == TRUE) {    /* Erase to EOL ? */
  592.         while (cp5!=cp1 && cp5[-1]==' ')
  593.             --cp5;
  594.  
  595.         if (cp3-cp5 <= 3)        /* Use only if erase is */
  596.             cp5 = cp3;        /* fewer characters. */
  597.     }
  598.  
  599.     movecursor(row, cp1-&vline[0]);    /* Go to start of line. */
  600.  
  601.     while (cp1 != cp5) {        /* Ordinary. */
  602.         (*term.t_putchar)(*cp1);
  603.         ++ttcol;
  604.         *cp2++ = *cp1++;
  605.     }
  606.  
  607.     if (cp5 != cp3) {        /* Erase. */
  608.         (*term.t_eeol)();
  609.         while (cp1 != cp3)
  610.             *cp2++ = *cp1++;
  611.     }
  612.     *flags &= ~VFCHG;        /* flag this line is changed */
  613. #endif
  614. }
  615.  
  616. /*
  617.  * Redisplay the mode line for the window pointed to by the "wp". This is the
  618.  * only routine that has any idea of how the modeline is formatted. You can
  619.  * change the modeline format by hacking at this routine. Called by "update"
  620.  * any time there is a dirty window.
  621.  */
  622. modeline(wp)
  623.     WINDOW *wp;
  624. {
  625.     register char *cp;
  626.     register int c;
  627.     register int n;        /* cursor position count */
  628.     register BUFFER *bp;
  629.     register i;            /* loop index */
  630.     register lchar;        /* character to draw line in buffer with */
  631.     register firstm;        /* is this the first mode? */
  632.     char tline[NLINE];        /* buffer for part of mode line */
  633.  
  634.     n = wp->w_toprow+wp->w_ntrows;      /* Location. */
  635.     vscreen[n]->v_flag |= VFCHG;        /* Redraw next time. */
  636.     vtmove(n, 0);                       /* Seek to right line. */
  637.     if (wp == curwp)            /* mark the current buffer */
  638.     lchar = '=';
  639.     else
  640. #if    REVSTA
  641.     if (revexist)
  642.         lchar = ' ';
  643.     else
  644. #endif
  645.         lchar = '-';
  646.  
  647.     vtputc(lchar);
  648.     bp = wp->w_bufp;
  649.  
  650.     if ((bp->b_flag&BFCHG) != 0)                /* "*" if changed. */
  651.         vtputc('*');
  652.     else
  653.         vtputc(lchar);
  654.  
  655.     n  = 2;
  656.     strcpy(tline, " MicroEMACS 3.6 (");        /* Buffer name. */
  657.  
  658.     /* display the modes */
  659.  
  660.     firstm = TRUE;
  661.     for (i = 0; i < NUMMODES; i++)    /* add in the mode flags */
  662.         if (wp->w_bufp->b_mode & (1 << i)) {
  663.             if (firstm != TRUE)
  664.                 strcat(tline, " ");
  665.             firstm = FALSE;
  666.             strcat(tline, modename[i]);
  667.         }
  668.     strcat(tline,") ");
  669.  
  670.     cp = &tline[0];
  671.     while ((c = *cp++) != 0)
  672.         {
  673.         vtputc(c);
  674.         ++n;
  675.         }
  676.  
  677.     vtputc(lchar);
  678.     vtputc(lchar);
  679.     vtputc(' ');
  680.     n += 3;
  681.     cp = &bp->b_bname[0];
  682.  
  683.     while ((c = *cp++) != 0)
  684.         {
  685.         vtputc(c);
  686.         ++n;
  687.         }
  688.  
  689.     vtputc(' ');
  690.     vtputc(lchar);
  691.     vtputc(lchar);
  692.     n += 3;
  693.  
  694.     if (bp->b_fname[0] != 0)            /* File name. */
  695.         {
  696.     vtputc(' ');
  697.     ++n;
  698.         cp = "File: ";
  699.  
  700.         while ((c = *cp++) != 0)
  701.             {
  702.             vtputc(c);
  703.             ++n;
  704.             }
  705.  
  706.         cp = &bp->b_fname[0];
  707.  
  708.         while ((c = *cp++) != 0)
  709.             {
  710.             vtputc(c);
  711.             ++n;
  712.             }
  713.  
  714.         vtputc(' ');
  715.         ++n;
  716.         }
  717.  
  718. #if WFDEBUG
  719.     vtputc(lchar);
  720.     vtputc((wp->w_flag&WFMODE)!=0  ? 'M' : lchar);
  721.     vtputc((wp->w_flag&WFHARD)!=0  ? 'H' : lchar);
  722.     vtputc((wp->w_flag&WFEDIT)!=0  ? 'E' : lchar);
  723.     vtputc((wp->w_flag&WFMOVE)!=0  ? 'V' : lchar);
  724.     vtputc((wp->w_flag&WFFORCE)!=0 ? 'F' : lchar);
  725.     n += 6;
  726. #endif
  727.  
  728.     while (n < term.t_ncol)             /* Pad to full width. */
  729.         {
  730.         vtputc(lchar);
  731.         ++n;
  732.         }
  733. }
  734.  
  735. upmode()    /* update all the mode lines */
  736.  
  737. {
  738.     register WINDOW *wp;
  739.  
  740.     wp = wheadp;
  741.     while (wp != NULL) {
  742.         wp->w_flag |= WFMODE;
  743.         wp = wp->w_wndp;
  744.     }
  745. }
  746.  
  747. /*
  748.  * Send a command to the terminal to move the hardware cursor to row "row"
  749.  * and column "col". The row and column arguments are origin 0. Optimize out
  750.  * random calls. Update "ttrow" and "ttcol".
  751.  */
  752. movecursor(row, col)
  753.     {
  754.     if (row!=ttrow || col!=ttcol)
  755.         {
  756.         ttrow = row;
  757.         ttcol = col;
  758.         (*term.t_move)(row, col);
  759.         }
  760.     }
  761.  
  762. /*
  763.  * Erase the message line. This is a special routine because the message line
  764.  * is not considered to be part of the virtual screen. It always works
  765.  * immediately; the terminal buffer is flushed via a call to the flusher.
  766.  */
  767. mlerase()
  768.     {
  769.     int i;
  770.     
  771.     movecursor(term.t_nrow, 0);
  772.     if (eolexist == TRUE)
  773.         (*term.t_eeol)();
  774.     else {
  775.         for (i = 0; i < term.t_ncol - 1; i++)
  776.             (*term.t_putchar)(' ');
  777.         movecursor(term.t_nrow, 1);    /* force the move! */
  778.         movecursor(term.t_nrow, 0);
  779.     }
  780.     (*term.t_flush)();
  781.     mpresf = FALSE;
  782.     }
  783.  
  784. /*
  785.  * Ask a yes or no question in the message line. Return either TRUE, FALSE, or
  786.  * ABORT. The ABORT status is returned if the user bumps out of the question
  787.  * with a ^G. Used any time a confirmation is required.
  788.  */
  789.  
  790. mlyesno(prompt)
  791.  
  792. char *prompt;
  793.  
  794. {
  795.     char c;            /* input character */
  796.     char buf[NPAT];        /* prompt to user */
  797.  
  798.     for (;;) {
  799.         /* build and prompt the user */
  800.         strcpy(buf, prompt);
  801.         strcat(buf, " [y/n]? ");
  802.         mlwrite(buf);
  803.  
  804.         /* get the responce */
  805.         c = (*term.t_getchar)();
  806.  
  807.         if (c == BELL)        /* Bail out! */
  808.             return(ABORT);
  809.  
  810.         if (c=='y' || c=='Y')
  811.             return(TRUE);
  812.  
  813.         if (c=='n' || c=='N')
  814.             return(FALSE);
  815.     }
  816. }
  817.  
  818. /*
  819.  * Write a prompt into the message line, then read back a response. Keep
  820.  * track of the physical position of the cursor. If we are in a keyboard
  821.  * macro throw the prompt away, and return the remembered response. This
  822.  * lets macros run at full speed. The reply is always terminated by a carriage
  823.  * return. Handle erase, kill, and abort keys.
  824.  */
  825.  
  826. mlreply(prompt, buf, nbuf)
  827.     char *prompt;
  828.     char *buf;
  829. {
  830.     return(mlreplyt(prompt,buf,nbuf,'\n'));
  831. }
  832.  
  833. /*    A more generalized prompt/reply function allowing the caller
  834.     to specify the proper terminator. If the terminator is not
  835.     a return ('\n') it will echo as "<NL>"
  836.                             */
  837. mlreplyt(prompt, buf, nbuf, eolchar)
  838.  
  839. char *prompt;
  840. char *buf;
  841. char eolchar;
  842.  
  843. {
  844.     register int cpos;
  845.     register int i;
  846.     register int c;
  847.  
  848.     cpos = 0;
  849.  
  850.     if (kbdmop != NULL) {
  851.         while ((c = *kbdmop++) != '\0')
  852.             buf[cpos++] = c;
  853.  
  854.         buf[cpos] = 0;
  855.  
  856.         if (buf[0] == 0)
  857.             return(FALSE);
  858.  
  859.         return(TRUE);
  860.     }
  861.  
  862.     /* check to see if we are executing a command line */
  863.     if (clexec) {
  864.         nxtarg(buf);
  865.         return(TRUE);
  866.     }
  867.  
  868.     mlwrite(prompt);
  869.  
  870.     for (;;) {
  871.     /* get a character from the user. if it is a <ret>, change it
  872.        to a <NL>                            */
  873.         c = (*term.t_getchar)();
  874.         if (c == 0x0d)
  875.             c = '\n';
  876.  
  877.         if (c == eolchar) {
  878.             buf[cpos++] = 0;
  879.  
  880.             if (kbdmip != NULL) {
  881.                 if (kbdmip+cpos > &kbdm[NKBDM-3]) {
  882.                     ctrlg(FALSE, 0);
  883.                     (*term.t_flush)();
  884.                     return(ABORT);
  885.                 }
  886.  
  887.                 for (i=0; i<cpos; ++i)
  888.                     *kbdmip++ = buf[i];
  889.                 }
  890.  
  891.                 (*term.t_putchar)('\r');
  892.                 ttcol = 0;
  893.                 (*term.t_flush)();
  894.  
  895.                 if (buf[0] == 0)
  896.                     return(FALSE);
  897.  
  898.                 return(TRUE);
  899.  
  900.             } else if (c == 0x07) {    /* Bell, abort */
  901.                 (*term.t_putchar)('^');
  902.                 (*term.t_putchar)('G');
  903.                 ttcol += 2;
  904.                 ctrlg(FALSE, 0);
  905.                 (*term.t_flush)();
  906.                 return(ABORT);
  907.  
  908.             } else if (c == 0x7F || c == 0x08) {    /* rubout/erase */
  909.                 if (cpos != 0) {
  910.                     (*term.t_putchar)('\b');
  911.                     (*term.t_putchar)(' ');
  912.                     (*term.t_putchar)('\b');
  913.                     --ttcol;
  914.  
  915.                     if (buf[--cpos] < 0x20) {
  916.                         (*term.t_putchar)('\b');
  917.                         (*term.t_putchar)(' ');
  918.                         (*term.t_putchar)('\b');
  919.                         --ttcol;
  920.                     }
  921.  
  922.                     if (buf[cpos] == '\n') {
  923.                         (*term.t_putchar)('\b');
  924.                         (*term.t_putchar)('\b');
  925.                         (*term.t_putchar)(' ');
  926.                         (*term.t_putchar)(' ');
  927.                         (*term.t_putchar)('\b');
  928.                         (*term.t_putchar)('\b');
  929.                         --ttcol;
  930.                         --ttcol;
  931.                     }
  932.  
  933.                     (*term.t_flush)();
  934.                 }
  935.  
  936.             } else if (c == 0x15) {    /* C-U, kill */
  937.                 while (cpos != 0) {
  938.                     (*term.t_putchar)('\b');
  939.                     (*term.t_putchar)(' ');
  940.                     (*term.t_putchar)('\b');
  941.                     --ttcol;
  942.  
  943.                     if (buf[--cpos] < 0x20) {
  944.                         (*term.t_putchar)('\b');
  945.                         (*term.t_putchar)(' ');
  946.                         (*term.t_putchar)('\b');
  947.                         --ttcol;
  948.                     }
  949.                 }
  950.  
  951.                 (*term.t_flush)();
  952.  
  953.             } else {
  954.                 if (cpos < nbuf-1) {
  955.                     buf[cpos++] = c;
  956.  
  957.                     if ((c < ' ') && (c != '\n')) {
  958.                         (*term.t_putchar)('^');
  959.                         ++ttcol;
  960.                         c ^= 0x40;
  961.                     }
  962.  
  963.                     if (c != '\n')
  964.                         (*term.t_putchar)(c);
  965.                     else {    /* put out <NL> for <ret> */
  966.                         (*term.t_putchar)('<');
  967.                         (*term.t_putchar)('N');
  968.                         (*term.t_putchar)('L');
  969.                         (*term.t_putchar)('>');
  970.                         ttcol += 3;
  971.                     }
  972.                 ++ttcol;
  973.                 (*term.t_flush)();
  974.             }
  975.         }
  976.     }
  977. }
  978.  
  979. /*
  980.  * Write a message into the message line. Keep track of the physical cursor
  981.  * position. A small class of printf like format items is handled. Assumes the
  982.  * stack grows down; this assumption is made by the "++" in the argument scan
  983.  * loop. Set the "message line" flag TRUE.
  984.  */
  985.  
  986. mlwrite(fmt, arg)
  987.     char *fmt;
  988.     {
  989.     register int c;
  990.     register char *ap;
  991.  
  992.     if (eolexist == FALSE) {
  993.         mlerase();
  994.         (*term.t_flush)();
  995.     }
  996.  
  997.     movecursor(term.t_nrow, 0);
  998.     ap = (char *) &arg;
  999.     while ((c = *fmt++) != 0) {
  1000.         if (c != '%') {
  1001.             (*term.t_putchar)(c);
  1002.             ++ttcol;
  1003.             }
  1004.         else
  1005.             {
  1006.             c = *fmt++;
  1007.             switch (c) {
  1008.                 case 'd':
  1009.                     mlputi(*(int *)ap, 10);
  1010.                     ap += sizeof(int);
  1011.                     break;
  1012.  
  1013.                 case 'o':
  1014.                     mlputi(*(int *)ap,  8);
  1015.                     ap += sizeof(int);
  1016.                     break;
  1017.  
  1018.                 case 'x':
  1019.                     mlputi(*(int *)ap, 16);
  1020.                     ap += sizeof(int);
  1021.                     break;
  1022.  
  1023.                 case 'D':
  1024.                     mlputli(*(long *)ap, 10);
  1025.                     ap += sizeof(long);
  1026.                     break;
  1027.  
  1028.                 case 's':
  1029.                     mlputs(*(char **)ap);
  1030.                     ap += sizeof(char *);
  1031.                     break;
  1032.  
  1033.                 default:
  1034.                     (*term.t_putchar)(c);
  1035.                     ++ttcol;
  1036.                 }
  1037.             }
  1038.         }
  1039.     if (eolexist == TRUE)
  1040.         (*term.t_eeol)();
  1041.     (*term.t_flush)();
  1042.     mpresf = TRUE;
  1043.     }
  1044.  
  1045. /*
  1046.  * Write out a string. Update the physical cursor position. This assumes that
  1047.  * the characters in the string all have width "1"; if this is not the case
  1048.  * things will get screwed up a little.
  1049.  */
  1050. mlputs(s)
  1051.     char *s;
  1052.     {
  1053.     register int c;
  1054.  
  1055.     while ((c = *s++) != 0)
  1056.         {
  1057.         (*term.t_putchar)(c);
  1058.         ++ttcol;
  1059.         }
  1060.     }
  1061.  
  1062. /*
  1063.  * Write out an integer, in the specified radix. Update the physical cursor
  1064.  * position. This will not handle any negative numbers; maybe it should.
  1065.  */
  1066. mlputi(i, r)
  1067.     {
  1068.     register int q;
  1069.     static char hexdigits[] = "0123456789ABCDEF";
  1070.  
  1071.     if (i < 0)
  1072.         {
  1073.         i = -i;
  1074.         (*term.t_putchar)('-');
  1075.         }
  1076.  
  1077.     q = i/r;
  1078.  
  1079.     if (q != 0)
  1080.         mlputi(q, r);
  1081.  
  1082.     (*term.t_putchar)(hexdigits[i%r]);
  1083.     ++ttcol;
  1084.     }
  1085.  
  1086. /*
  1087.  * do the same except as a long integer.
  1088.  */
  1089. mlputli(l, r)
  1090.     long l;
  1091.     {
  1092.     register long q;
  1093.  
  1094.     if (l < 0)
  1095.         {
  1096.         l = -l;
  1097.         (*term.t_putchar)('-');
  1098.         }
  1099.  
  1100.     q = l/r;
  1101.  
  1102.     if (q != 0)
  1103.         mlputli(q, r);
  1104.  
  1105.     (*term.t_putchar)((int)(l%r)+'0');
  1106.     ++ttcol;
  1107.     }
  1108.  
  1109. #if RAINBOW
  1110.  
  1111. putline(row, col, buf)
  1112.     int row, col;
  1113.     char buf[];
  1114.     {
  1115.     int n;
  1116.  
  1117.     n = strlen(buf);
  1118.     if (col + n - 1 > term.t_ncol)
  1119.         n = term.t_ncol - col + 1;
  1120.     Put_Data(row, col, n, buf);
  1121.     }
  1122. #endif
  1123.  
  1124. /* get a command name from the command line. Command completion means
  1125.    that pressing a <SPACE> will attempt to complete an unfinished command
  1126.    name if it is unique.
  1127. */
  1128.  
  1129. int (*getname())()
  1130.  
  1131. {
  1132.     register int cpos;    /* current column on screen output */
  1133.     register int c;
  1134.     register char *sp;    /* pointer to string for output */
  1135.     register NBIND *ffp;    /* first ptr to entry in name binding table */
  1136.     register NBIND *cffp;    /* current ptr to entry in name binding table */
  1137.     register NBIND *lffp;    /* last ptr to entry in name binding table */
  1138.     char buf[NSTRING];    /* buffer to hold tentative command name */
  1139.     int (*fncmatch())();
  1140.  
  1141.     /* starting at the begining of the string buffer */
  1142.     cpos = 0;
  1143.  
  1144.     /* if we are executing a keyboard macro, fill our buffer from there,
  1145.        and attempt a straight match */
  1146.     if (kbdmop != NULL) {
  1147.         while ((c = *kbdmop++) != '\0')
  1148.             buf[cpos++] = c;
  1149.  
  1150.         buf[cpos] = 0;
  1151.  
  1152.         /* return the result of a match */
  1153.         return(fncmatch(&buf[0]));
  1154.     }
  1155.  
  1156.     /* if we are executing a command line get the next arg and match it */
  1157.     if (clexec) {
  1158.         nxtarg(buf);
  1159.         return(fncmatch(&buf[0]));
  1160.     }
  1161.  
  1162.     /* build a name string from the keyboard */
  1163.     while (TRUE) {
  1164.         c = (*term.t_getchar)();
  1165.  
  1166.         /* if we are at the end, just match it */
  1167.         if (c == 0x0d) {
  1168.             buf[cpos] = 0;
  1169.  
  1170.             /* save keyboard macro string if needed */
  1171.             if (kbdtext(&buf[0]) == ABORT)
  1172.                 return( (int (*)()) NULL);
  1173.  
  1174.             /* and match it off */
  1175.             return(fncmatch(&buf[0]));
  1176.  
  1177.         } else if (c == 0x07) {    /* Bell, abort */
  1178.             (*term.t_putchar)('^');
  1179.             (*term.t_putchar)('G');
  1180.             ttcol += 2;
  1181.             ctrlg(FALSE, 0);
  1182.             (*term.t_flush)();
  1183.             return( (int (*)()) NULL);
  1184.  
  1185.         } else if (c == 0x7F || c == 0x08) {    /* rubout/erase */
  1186.             if (cpos != 0) {
  1187.                 (*term.t_putchar)('\b');
  1188.                 (*term.t_putchar)(' ');
  1189.                 (*term.t_putchar)('\b');
  1190.                 --ttcol;
  1191.                 --cpos;
  1192.                 (*term.t_flush)();
  1193.             }
  1194.  
  1195.         } else if (c == 0x15) {    /* C-U, kill */
  1196.             while (cpos != 0) {
  1197.                 (*term.t_putchar)('\b');
  1198.                 (*term.t_putchar)(' ');
  1199.                 (*term.t_putchar)('\b');
  1200.                 --cpos;
  1201.                 --ttcol;
  1202.             }
  1203.  
  1204.             (*term.t_flush)();
  1205.  
  1206.         } else if (c == ' ') {
  1207. /* <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< */
  1208.     /* attempt a completion */
  1209.     buf[cpos] = 0;        /* terminate it for us */
  1210.     ffp = &names[0];    /* scan for matches */
  1211.     while (ffp->n_func != NULL) {
  1212.         if (strncmp(buf, ffp->n_name, strlen(buf)) == 0) {
  1213.             /* a possible match! More than one? */
  1214.             if ((ffp + 1)->n_func == NULL ||
  1215.                (strncmp(buf, (ffp+1)->n_name, strlen(buf)) != 0)) {
  1216.                 /* no...we match, print it */
  1217.                 sp = ffp->n_name + cpos;
  1218.                 while (*sp)
  1219.                     (*term.t_putchar)(*sp++);
  1220.                 (*term.t_flush)();
  1221.                 return(ffp->n_func);
  1222.             } else {
  1223. /* << << << << << << << << << << << << << << << << << */
  1224.     /* try for a partial match against the list */
  1225.  
  1226.     /* first scan down until we no longer match the current input */
  1227.     lffp = (ffp + 1);
  1228.     while ((lffp+1)->n_func != NULL) {
  1229.         if (strncmp(buf, (lffp+1)->n_name, strlen(buf)) != 0)
  1230.             break;
  1231.         ++lffp;
  1232.     }
  1233.  
  1234.     /* and now, attempt to partial complete the string, char at a time */
  1235.     while (TRUE) {
  1236.         /* add the next char in */
  1237.         buf[cpos] = ffp->n_name[cpos];
  1238.  
  1239.         /* scan through the candidates */
  1240.         cffp = ffp + 1;
  1241.         while (cffp <= lffp) {
  1242.             if (cffp->n_name[cpos] != buf[cpos])
  1243.                 goto onward;
  1244.             ++cffp;
  1245.         }
  1246.  
  1247.         /* add the character */
  1248.         (*term.t_putchar)(buf[cpos++]);
  1249.     }
  1250. /* << << << << << << << << << << << << << << << << << */
  1251.             }
  1252.         }
  1253.         ++ffp;
  1254.     }
  1255.  
  1256.     /* no match.....beep and onward */
  1257.     (*term.t_beep)();
  1258. onward:;
  1259.     (*term.t_flush)();
  1260. /* <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< */
  1261.         } else {
  1262.             if (cpos < NSTRING-1 && c > ' ') {
  1263.                 buf[cpos++] = c;
  1264.                 (*term.t_putchar)(c);
  1265.             }
  1266.  
  1267.             ++ttcol;
  1268.             (*term.t_flush)();
  1269.         }
  1270.     }
  1271. }
  1272.  
  1273. kbdtext(buf)    /* add this text string to the current keyboard macro
  1274.            definition                        */
  1275.  
  1276. char *buf;    /* text to add to keyboard macro */
  1277.  
  1278. {
  1279.     /* if we are defining a keyboard macro, save it */
  1280.     if (kbdmip != NULL) {
  1281.         if (kbdmip+strlen(buf) > &kbdm[NKBDM-4]) {
  1282.             ctrlg(FALSE, 0);
  1283.             (*term.t_flush)();
  1284.             return(ABORT);
  1285.         }
  1286.  
  1287.         /* copy string in and null terminate it */
  1288.         while (*buf)
  1289.             *kbdmip++ = *buf++;
  1290.         *kbdmip++ = 0;
  1291.     }
  1292.     return(TRUE);
  1293. }
  1294.